import logging
from pynput import keyboard
from playsound import playsound
from threading import Thread, Event
import argparse
from pathlib import Path
from queue import Queue

# 配置日志记录
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[logging.FileHandler("keylog.log"), logging.StreamHandler()]
)


class AudioPlayer:
    """音频播放管理器，确保线程安全且不重复播放"""

    def __init__(self, sound_file):
        self.sound_file = sound_file
        self.queue = Queue()
        self.thread = Thread(target=self._player_loop, daemon=True)
        self.is_playing = False
        self.start()

    def start(self):
        """启动音频播放线程"""
        self.thread.start()

    def play(self):
        """请求播放音频"""
        self.queue.put(None)  # 放入一个空消息表示播放请求

    def _player_loop(self):
        """音频播放循环，处理队列中的请求"""
        while True:
            self.queue.get()  # 等待请求

            # 检查是否已经在播放，避免重复
            if not self.is_playing:
                try:
                    self.is_playing = True
                    logging.info(f"播放提示音: {self.sound_file}")
                    playsound(self.sound_file)
                except Exception as e:
                    logging.error(f"播放声音时出错: {e}")
                finally:
                    self.is_playing = False
                    self.queue.task_done()
            else:
                # 已经在播放，忽略此次请求
                self.queue.task_done()


class KeyCounter:
    def __init__(self, sound_file='tip.mp3', interval=20):
        self.count = 0
        self.interval = interval
        self.sound_file = Path(sound_file)
        self.stop_event = Event()

        # 验证声音文件是否存在
        if not self.sound_file.exists():
            logging.error(f"声音文件不存在: {self.sound_file}")
            raise FileNotFoundError(f"找不到声音文件: {sound_file}")

        # 创建音频播放器
        self.audio_player = AudioPlayer(str(self.sound_file))

        # 创建键盘监听器
        self.listener = keyboard.Listener(on_release=self.on_release)

    def on_release(self, key):
        """按键释放时的回调函数"""
        if self.stop_event.is_set():
            return False  # 停止监听

        try:
            key_char = key.char  # 普通字符键
        except AttributeError:
            key_char = str(key)  # 特殊键，如Ctrl、Alt等

        self.count += 1
        logging.info(f"按键 {key_char} - 总计数: {self.count}")

        # 每按指定次数播放提示音
        if self.count % self.interval == 0:
            self.audio_player.play()

    def start(self):
        """启动键盘监听"""
        logging.info(f"开始键盘监听 - 每 {self.interval} 次按键播放一次提示音")
        self.listener.start()
        try:
            # 等待停止事件
            self.stop_event.wait()
        except KeyboardInterrupt:
            logging.info("用户中断，程序退出")
        finally:
            self.stop()

    def stop(self):
        """停止键盘监听"""
        if not self.stop_event.is_set():
            self.stop_event.set()

        if self.listener.is_alive():
            # 等待监听器线程自然结束
            self.listener.join()

        logging.info(f"键盘监听已停止 - 总计按键次数: {self.count}")


def main():
    """主函数，处理命令行参数并启动程序"""
    parser = argparse.ArgumentParser(description='按键计数并播放提示音')
    parser.add_argument('-s', '--sound', default='tip.mp3', help='提示音文件路径')
    parser.add_argument('-i', '--interval', type=int, default=20, help='播放提示音的按键间隔')
    args = parser.parse_args()

    try:
        counter = KeyCounter(sound_file=args.sound, interval=args.interval)
        counter.start()
    except Exception as e:
        logging.critical(f"程序启动失败: {e}")
        exit(1)
if __name__ == "__main__":
    main()